﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.IO;

namespace Crap4Net.CCParsers
{
    public class ReflectorParser : ICCParser
    {
        private IList<CCDataEntry> _data;

        IDictionary<string, List<string>> _knownMethods = new Dictionary<string,List<String>>();

        #region ICCParser Members
        public IList<CCDataEntry> Data
        {
            get
            {
                if (_data == null)
                    throw new ApplicationException("Data was not loaded");
                return _data;
            }
            private set
            {
                _data = value;
            }
        }

        public void LoadData(string reportFileName)
        {
            if (!File.Exists(reportFileName))
            {
                throw new ArgumentException("Reflector report file was not found: " + reportFileName);
            }
            Data = new List<CCDataEntry>();
            ParseDataFromXml(reportFileName);
        }

        private bool MethodIsNew(string typeName, string methodName)
        {
            List<string> methods;
            var foundType = _knownMethods.TryGetValue(typeName, out methods);
            if (foundType == false)
            {
                methods = new List<String>(){methodName};
                _knownMethods[typeName] = methods;
                return true;
            }
            if (!methods.Contains(methodName))
            {
                methods.Add(methodName);
                return true;
            }
            else
            {
                return false;
            }
        }
        private void ParseDataFromXml(string reportFileName)
        {
            IEnumerable<XElement> results = LoadMethodsDataFromFile(reportFileName);
            foreach (var method in results)
            {
                var methodSignature = ParseMethodSignature(method);
                int coverage = GetCCValue(method);

                Data.Add(new CCDataEntry(methodSignature, coverage));
            }
        }

        private IEnumerable<XElement> LoadMethodsDataFromFile(string reportFileName)
        {
            XDocument loaded = XDocument.Load(reportFileName);
            var result = (from metric in loaded.Descendants()
                          where metric.Name.LocalName == "Method"
                          select metric);
            return result;
        }

        private MethodSignature ParseMethodSignature(XElement method)
        {
            string assembly = GetAssemblyName(method);
            string nameSpace = GetNameSpace(method);
            string typeName = GetTypeNameWithoutNameSpace(method);
            string methodName = GetMethodName(method);
            string[] args = GetArgs(method);
            var methodSignature = new MethodSignature(assembly, nameSpace, typeName, methodName, args);
            return methodSignature;
        }

        private string GetAssemblyName(XElement method)
        {
            return "unknown";
            //have no good way to get this info
            //as it seem only way is to:
            //use the numbers of type in each moduel and count undesrtands which type go where
            //and than extarct type name from method name to cross reference against this.
            throw new NotImplementedException();
        }
        private string GetNameSpace(XElement method)
        {
            var fullMethodName = (string)method.Attribute("Name");
            return ReflectorSignatureParser.GetNameSpace(fullMethodName);
        }
        private string GetTypeNameWithoutNameSpace(XElement method)
        {
            var fullMethodName = (string)method.Attribute("Name");
            return ReflectorSignatureParser.GetClassName(fullMethodName);
        }


        private string GetMethodName(XElement method)
        {
            var fullMethodName = (string)method.Attribute("Name");
            return ReflectorSignatureParser.GetMethodName(fullMethodName);
        }

        private string[] GetArgs(XElement method)
        {
            var fullMethodName = (string)method.Attribute("Name");
            int start = fullMethodName.IndexOf('(');
            int end = fullMethodName.IndexOf(')');
            var argString = fullMethodName.Substring(start + 1, end - start - 1);
            if (string.IsNullOrEmpty(argString))
                return new string[] { };
            else
                return argString.Split(new char[] { ',' });
        }
        private int GetCCValue(XElement method)
        {
            return (int)method.Attribute("CyclomaticComplexity");
        }
        #endregion
    }
}
